﻿using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using TasksDataXml;
using TasksDataXml.Data;
using TasksDataXml.Exceptions;

namespace TestFileController
{
    [TestFixture]
    public class FileControllerTests
    {
        private Controller _controller;
        private const string ROOT_FOLDER = @"d:\mine\sources\desktop\DropBoxToDo\TestFileController\TestData\";
        private string _storagePath;

        [SetUp]
        public void Init()
        {
            _controller = new Controller(ROOT_FOLDER);
            _storagePath = GeneratePath();
        }

        [TearDown]
        public void TearDown()
        {
            var dir = Path.GetDirectoryName(_storagePath);
            if (dir != null && Directory.Exists(dir))
                Directory.Delete(dir, true);

            _storagePath = null;
        }

        private string GeneratePath()
        {
            return TasksDataXml.Helpers.PathUtility.GetCategoriesPath(ROOT_FOLDER);
        }

        [Test]
        public void GenerateEmptyContainerTest()
        {
            CommonException ex;
            var empty = _controller.InitAsEmptyContainer(_storagePath, out ex);

            Assert.IsNotNull(empty, "Generation of empty container is failed");
            Assert.IsNull(ex, "CommonException is thrown");

            var path = GeneratePath();
            path = Path.GetDirectoryName(path);
            if (path != null) 
                path = Path.Combine(path, empty.Filename);

            Assert.IsTrue(File.Exists(path), "File is not created");
        }

        [Test]
        public void SaveEmptyContainerTest()
        {
            CommonException ex;
            var empty = _controller.InitAsEmptyContainer(_storagePath, out ex);
            _controller.SaveData(out ex);

            Assert.IsNull(ex, "CommonException is thrown");

            var path = GeneratePath();
            path = Path.GetDirectoryName(path);
            if (path != null) 
                path = Path.Combine(path, empty.Filename);
            Assert.IsTrue(File.Exists(path), "File is not created");
        }

        [Test]
        public void SaveNotEmptyContainerTest()
        {
            CommonException ex;
            var empty = _controller.InitAsEmptyContainer(_storagePath, out ex);

            FillContainer(empty);
            
            _controller.SaveData(out ex);

            Assert.IsNull(ex, "CommonException is thrown");

            var path = GeneratePath();
            path = Path.GetDirectoryName(path);
            if (path != null) 
                path = Path.Combine(path, empty.Filename);
            Assert.IsTrue(File.Exists(path), "File is not created");
        }

        private Tuple<int/*total num of categories*/, int/*total num of tasks*/> FillContainer(CategoryContainer cnt)
        {
            int numOfCats = 1;
            int numOfTasks = 0;
            var category = new Category();
            category.GuidId = Guid.NewGuid();
            category.Name = "Empty";
            
            var data = new Task();
            data.GuidId = Guid.NewGuid();
            data.Desc = "Description";
            data.IsCompleted = false;
            data.IssueDate = DateTime.Now;

            category.AddTask(data);
            ++numOfTasks;

            cnt.CategoryList[0].AddCategory(category);
            ++numOfCats;

            cnt.CategoryList[0].AddTask(data);
            ++numOfTasks;
            
            Assert.IsTrue(category.Tasks.Count == 1, "1 task has to be added");
            Assert.IsTrue(cnt.CategoryList.Count == 1, "1 category has to be added in the root");
            Assert.IsTrue(cnt.CategoryList[0].CategoryList.Count == 1, "1 category has to be added to the root's category");
            Assert.IsTrue(cnt.CategoryList[0].Tasks.Count == 1, "1 task has to be added to the root category");

            return new Tuple<int, int>(numOfCats, numOfTasks);
        }

        [Test]
        public void SaveCategoryTasksTest()
        {
            CommonException ex;
            var empty = _controller.InitAsEmptyContainer(_storagePath, out ex);

            FillContainer(empty);

            var root = _controller.Container.GetFolder(ROOT_FOLDER);
            var exceptions = new List<CommonException>();
            var serializer = new TasksDataXml.Data.DataSerializer<Task>();
            foreach (var category in _controller.Container.CategoryList)
            {
                category.SaveTasks(serializer, root, exceptions);
            }

            Assert.IsEmpty(exceptions, "Save category task is failed");
        }

        /*
         * note
         * all operations with task path has to be done on save. Until file is saved, idon't add 
         * a task path, it has to be generated on save
         * this means, i have to save the whole proj, not single task
         * it'll be consistent
         */
        [Test]
        public void SaveLoadCategoryTasksTest()
        {
            CommonException ex;
            var empty = _controller.InitAsEmptyContainer(_storagePath, out ex);
            
            var t = FillContainer(empty);

            _controller.SaveData(out ex);
            
            Assert.IsNull(ex, "Save category task is failed", ex);

            _controller.LoadData(out ex, ROOT_FOLDER);

            Assert.IsNull(ex, ex == null ? string.Empty : ex.Message);

            Tuple<int, int> t1 = CountCategoriesAndTasks(_controller.Container.CategoryList);

            Assert.AreEqual(t, t1, "Loaded values(Category and Tasks) are not the same");

            t1 = CountCategoriesAndTaskFileNames(_controller.Container.CategoryList);

            Assert.AreEqual(t, t1, "Loaded values(Category and TaskPaths) are not the same");
        }

        private Tuple<int, int> CountCategoriesAndTasks(List<Category> list)
        {
            int catNum = 0;
            int taskNum = 0;

            foreach (var c in list)
            {
                ++catNum;
                taskNum += c.Tasks.Count;
                
                var tuple = CountCategoriesAndTasks(c.CategoryList);
                catNum += tuple.Item1;
                taskNum += tuple.Item2;
            }

            return new Tuple<int, int>(catNum, taskNum);
        }

        private Tuple<int, int> CountCategoriesAndTaskFileNames(List<Category> list)
        {
            int catNum = 0;
            int taskNum = 0;

            foreach (var c in list)
            {
                ++catNum;
                taskNum += c.TaskPathList.Count;

                var tuple = CountCategoriesAndTasks(c.CategoryList);
                catNum += tuple.Item1;
                taskNum += tuple.Item2;
            }

            return new Tuple<int, int>(catNum, taskNum);
        }
    }
}
